﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;

namespace TimeWar.Engine
{
    public enum CameraType
    {
        Demo,
        FirstPerson,
        Chase,
    }

    public class Camera
    {

        #region Attributes

        #region All Cameras
        protected CameraType m_type;
        protected Matrix view;
        protected Matrix projection;
        protected float aspectRatio = 0;

        protected Vector3 up;
        protected float viewAngle = MathHelper.PiOver4;
        protected float nearClip = 0.00001f;
        protected float farClip = 5000.0f;
        protected Quaternion rotation;

        protected Vector3 position;  //the camera position
        protected BoundingFrustum _frustum;
        protected BoundingFrustum _reflectedFrustum;
        protected Matrix reflectedView;

        #endregion All Cameras

        #region Demo Camera
        protected float cameraArc = 0;
        protected float cameraRotation = 0;
        protected float cameraDistance = 2000;
        #endregion Demo Camera

        #region Chase Camera
        protected Vector3 prevObjPosition;
        protected Vector3 desiredPositionOffset = new Vector3(0, 20f, 250f);
        protected Vector3 objPosition;
        protected Quaternion objRotation;

        #endregion Chase Camera

        #endregion Attributes


        #region All Cameras

        public Matrix View
        {
            get { return view; }
        }

        public Matrix Projection
        {
            get { return projection; }
        }

        public Vector3 Up
        {
            get { return up; }
            set { up = value; }
        }

        public Matrix RotationMatrix
        {
            get
            {
                Matrix rotationMatrix = Matrix.CreateFromQuaternion(rotation);
                return rotationMatrix;
            }
        }

        public Vector3 Position
        {
            get { return position; }
            set { position = value; }
        }

        public float ViewAngle
        {
            get { return viewAngle; }
            set { viewAngle = value; }
        }

        public float NearClip
        {
            get { return nearClip; }
            set { nearClip = value; }
        }

        public float FarClip
        {
            get { return farClip; }
            set { farClip = value; }
        }

        public BoundingFrustum Frustum
        {
            get { return _frustum; }
        }

        public BoundingFrustum ReflectedFrustum
        {
            get { return _reflectedFrustum; }
        }

        public Matrix ReflectedView
        {
            get { return reflectedView; }
            set { reflectedView = value; }
        }

        public void setAspectRatio(float width, float height)
        {
            aspectRatio = width / height;
        }


        public Camera(CameraType cameraType)
        {
            m_type = cameraType;

            _frustum = new BoundingFrustum(Matrix.Identity);
            _reflectedFrustum = new BoundingFrustum(Matrix.Identity);

            Update(null);

        }

        protected void updateFrustum()
        {
            _frustum = new BoundingFrustum(Matrix.Multiply(view, projection));
            _reflectedFrustum = new BoundingFrustum(Matrix.Multiply(reflectedView, projection));
        }

        public void Update(GameTime gameTime)
        {

            if (m_type == CameraType.Demo)
            {
                // Compute camera matrices.
                view = Matrix.CreateTranslation(0, -40, 0) *
                       Matrix.CreateRotationY(MathHelper.ToRadians(cameraRotation)) *
                       Matrix.CreateRotationX(MathHelper.ToRadians(cameraArc)) *
                       Matrix.CreateLookAt(new Vector3(0, 0, -cameraDistance),
                                           new Vector3(0, 0, 0), Vector3.Up);

                projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4,
                                                                 aspectRatio,
                                                                 1,
                                                                 10000);

            }
            else if (m_type == CameraType.Chase)
            {
                position = Vector3.Add(desiredPositionOffset, objPosition);
                position = Vector3.Transform(position, Matrix.CreateFromQuaternion(objRotation));

                up = Vector3.Up;
                up = Vector3.Transform(up, Matrix.CreateFromQuaternion(objRotation));
                up.Normalize();

                view = Matrix.CreateLookAt(position, objPosition, up);
                projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4, aspectRatio, nearClip, farClip);

                if (prevObjPosition == objPosition)
                {
                    //ship hasn't moved.
                    //chasePosition.X++;
                }
                else
                {
                    prevObjPosition = objPosition;
                }

                updateFrustum();
            }


        }



        #endregion All Cameras

        #region Demo Camera only

        public float Arc
        {
            get { return cameraArc; }
            set { cameraArc = value; }
        }

        public float Rotation
        {
            get { return cameraRotation; }
            set { cameraRotation = value; }
        }

        public float Distance
        {
            get { return cameraDistance; }
            set { cameraDistance = value; }
        }

        #endregion Demo Camera only


        #region Chase Camera only
        public Vector3 DesiredPositionOffset
        {
            get { return desiredPositionOffset; }
            set { desiredPositionOffset = value; }
        }

        public void UpdateChasedObject(Vector3 chasedPosition, Quaternion chasedRotation)
        {
            objPosition = chasedPosition;
            objRotation = chasedRotation;
        }

        #endregion Chase Camera only


    }
}
